﻿#define PY_SSIZE_T_CLEAN
#include "Python.h"

#include "klogger/interface/logger.h"
#include "klogger/interface/logger_factory.h"
#include <cstdint>
#include <exception>
#include <memory>
#include <string>
#include <unordered_map>

std::unique_ptr<klogger::Logger> global_logger_ptr;
using AppenderMap =
    std::unordered_map<std::string, std::unique_ptr<klogger::Appender>>;
AppenderMap global_appender_map;

static auto NewAppender(PyObject * /*self*/, PyObject *args) -> PyObject * {
  if (!global_logger_ptr) {
    global_logger_ptr.reset(LoggerFactory::createLogger());
  }
  char *app_name{nullptr};
  char *log_config{nullptr};
  if (!PyArg_ParseTuple(args, "ss", &app_name, &log_config)) {
    Py_RETURN_FALSE;
  }
  if (!app_name || !log_config) {
    Py_RETURN_FALSE;
  }
  try {
    auto *appender_ptr = global_logger_ptr->newAppender(app_name, log_config);
    global_appender_map[app_name].reset(appender_ptr);
  } catch (std::exception &ex) {
    PyErr_SetString(PyExc_RuntimeError, ex.what());
    Py_RETURN_FALSE;
  }
  Py_RETURN_TRUE;
}

static auto DestroyAppender(PyObject * /*self*/, PyObject *args) -> PyObject * {
  if (!global_logger_ptr) {
    global_logger_ptr.reset(LoggerFactory::createLogger());
  }
  char *app_name{nullptr};
  if (!PyArg_ParseTuple(args, "s", &app_name)) {
    Py_RETURN_NONE;
  }
  if (!app_name) {
    Py_RETURN_NONE;
  }
  global_appender_map.erase(app_name);
  Py_RETURN_NONE;
}

static auto Write(PyObject * /*self*/, PyObject *args) -> PyObject * {
  if (!global_logger_ptr) {
    global_logger_ptr.reset(LoggerFactory::createLogger());
  }
  char *app_name{nullptr};
  int level{0};
  char *content{nullptr};
  if (!PyArg_ParseTuple(args, "sis", &app_name, &level, &content)) {
    Py_RETURN_NONE;
  }
  if (!app_name || !content) {
    Py_RETURN_NONE;
  }
  auto it = global_appender_map.find(app_name);
  if (it == global_appender_map.end()) {
    Py_RETURN_NONE;
  }
  it->second->write(level, "%s", content);
  Py_RETURN_NONE;
}

static PyMethodDef buffer_methods[] = {
    {"NewAppender", NewAppender, METH_VARARGS, "New logger appender"},
    {"DestroyAppender", DestroyAppender, METH_VARARGS,
     "Destroy logger appender"},
    {"Write", Write, METH_VARARGS, "Write log"},
    {nullptr, nullptr, 0, nullptr} /* Sentinel */
};

static void rpc_module_cleanup() { global_appender_map.clear(); }

#if !defined(_MSC_VER)
#if defined(DEBUG) || defined(_DEBUG)
#define INIT_FUNC PyMODINIT_FUNC PyInit_log_d(void)
#define MOD_NAME "log_d"
#else
#define INIT_FUNC PyMODINIT_FUNC PyInit_log(void)
#define MOD_NAME "log"
#endif // defined(DEBUG) || defined(_DEBUG)
#else
#define INIT_FUNC PyMODINIT_FUNC PyInit_log(void)
#define MOD_NAME "log"
#endif // !defined(_MSC_VER)

#if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 3

static struct PyModuleDef log_module = {PyModuleDef_HEAD_INIT, MOD_NAME,
                                        "Log framework", -1, buffer_methods};

INIT_FUNC {
  auto *m = PyModule_Create(&log_module);
  if (!m) {
    return nullptr;
  }
  if (Py_AtExit(rpc_module_cleanup) == -1) {
    return nullptr;
  }
  return m;
}

#else

INIT_FUNC {
  if (Py_InitModule(MOD_NAME, buffer_methods)) {
    Py_AtExit(rpc_module_cleanup);
  }
}

#endif // PY_MAJOR_VERSION >= 3
